use std::io::{Stdin, Stdout, Write};

use crate::{evaluator::Evaluator, lexer::Lexer, parser::Parser};

pub fn start(stdin: Stdin, mut stdout: Stdout) {
  let mut evaluator = Evaluator::new();

  loop {
    write!(stdout, "> ").expect("should have written prompt string >>");
    stdout.flush().expect("should have flushed stdout!");

    let mut input = String::new();

    if let Err(e) = stdin.read_line(&mut input) {
      writeln!(stdout, "Error: {e}").expect("should have written error message");
      return;
    }

    /*
    // Lexer
    let mut lexer = Lexer::new(input.as_str());
    loop {
      let token = lexer.next_token();
      if token.kind == TokenKind::Eof {
        break;
      }
      writeln!(stdout, "{token:?}").expect("Token should haven been writen");
    }
    */

    /*
    // Parser
    let lexer = Lexer::new(input.as_str());
    let mut parser = Parser::new(lexer);
    let program = parser.parse_program().expect("error parsing program");
    if parser.get_errors().len() != 0 {
      print_parse_errors(&stdout, parser.get_errors());
      continue;
    }

    // use crate::ast::Node;
    let parsed_program_string = program.print_string(); // 需要引入 ast::Node
    writeln!(stdout, "{parsed_program_string}")
      .expect("parsed program should be written to stdout");
    */

    // /*
    // Evaluator
    let lexer = Lexer::new(input.as_str());
    let mut parser = Parser::new(lexer);
    let program = parser.parse_program().expect("error parsing program");
    if parser.get_errors().len() != 0 {
      print_parse_errors(&stdout, parser.get_errors());
      continue;
    }
    let evaluated = evaluator.eval_program(program);
    writeln!(stdout, "{evaluated}").expect("parsed program should be written to stdout");
    // */
  }
}

fn print_parse_errors(mut stdout: &Stdout, errors: &Vec<String>) {
  writeln!(stdout, "(o.o) >^<)").unwrap();
  writeln!(stdout, "We ran into parser errors")
    .expect("error message info should be written to stdout");
  for error in errors {
    writeln!(stdout, "\t-> {error}").expect("error should be written to stdout");
  }
}
